"""
pandas对象拥有一组常用的数学和统计方法。它们大部分都属于约简和汇总统计，用于从Series中提取单个值（如sum或mean）或从DataFrame的行或列中提取一个Series。跟对应的NumPy数组方法相比，它们都是基于没有缺失数据的假设而构建的。

df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],[np.nan, np.nan], [0.75, -1.3]],index=['a', 'b', 'c', 'd'],columns=['one', 'two'])
print(df)
# 调用DataFrame的sum()方法 返回一个含有列的和的Series
print(df.sum())
# 传入axis='columns' 或 axis =1  表示按行进行求和
print(df.sum(axis='columns'))
# NA 值自动排除 使用skipna=False 可以保留NA值
print(df.mean(axis='columns', skipna=False))
# df.mean(axis='columns', skipna=False) 计算每一行的平均值
# 默认情况下 axis=0 表示按列进行
# skipna=False：
# 表示在计算时不跳过缺失值（NaN）。
# 如果某一行包含一个或多个 NaN 值，则该行的均值结果也会是 NaN

# axis 约简的轴 DataFrame的行用0，列用1
# skipna 排除缺失值 默认为True
# level 如果轴是层次化索引的 则根据level分组约简

print(df)

# idxmin 返回每一列最小值的索引 idxmax 返回每一列最大值的索引 达到最小值或最大值的索引 间接统计
df.idxmax()
print(df.cumsum()) # 累加函数
print(df.describe()) # 描述性统计用于一次性产生多个汇总统计

# 非数值型数据 describe 会产生另外一种汇总统计
obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
print(obj.describe())

count   非NA值的数量
describe    针对Series或各DataFrame列计算汇总统计
min、max 计算最小值和最大值
argmin、argmax   计算能够获取到最小值和最大值的索引位置（整数）
idxmin、idxmax   计算能够获取到最小值和最大值的索引值
quantile    计算样本的分位数(0到1)》
sum 值的总和
mean    值的平均数
median  值的算术中位数(50%分位数)
mad 根据平均值计算平均绝对离差
var 样本值的方差
std 样本值的标准差
skew    样本值的偏度（三阶矩）
kurt    样本值的峰度（四阶矩）
cumsum  样本值的累计和
cummin、cummax   样本值的累计最大值和累计最小值
cumprod 样本值的累计积
diff    计算一阶差分（对时间序列很有用）
pct_change  计算百分数变化

# pip install pandas-datareader 安装数据读取工具
all_data = {ticker: web.get_data_yahoo(ticker)
            for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}
#  all_data = {ticker: web.get_data_yahoo(ticker) for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}
# 这是一个字典推导式，用于从 Yahoo Finance 获取指定股票的历史数据。
# 具体步骤：
# ['AAPL', 'IBM', 'MSFT', 'GOOG']：
# 定义了一个包含多个股票代码的列表，表示需要获取的股票数据。
# web.get_data_yahoo(ticker)：
# 使用 pandas_datareader 库中的 get_data_yahoo 方法，根据股票代码（如 'AAPL'）从 Yahoo Finance 下载历史数据。
# 返回的是一个 Pandas DataFrame，包含股票的开盘价、收盘价、最高价、最低价、成交量等信息。
# {ticker: web.get_data_yahoo(ticker) for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}：
# 将每个股票代码作为键，对应的股票数据作为值，构建一个字典 all_data。
# 结果：
# all_data 是一个字典，结构如下：
#     {
#         'AAPL': DataFrame(包含 AAPL 的历史数据),
#         'IBM': DataFrame(包含 IBM 的历史数据),
#         'MSFT': DataFrame(包含 MSFT 的历史数据),
#         'GOOG': DataFrame(包含 GOOG 的历史数据)
#     }


price = pd.DataFrame({ticker: data['Adj Close']
                     for ticker, data in all_data.items()})
# price = pd.DataFrame({ticker: data['Adj Close'] for ticker, data in all_data.items()})
# 提取每只股票的调整后收盘价（Adj Close），并将其存储到一个新的 DataFrame 中。
# 具体步骤：
# {ticker: data['Adj Close'] for ticker, data in all_data.items()}：
# 遍历 all_data 字典，提取每只股票的 Adj Close 列（调整后收盘价）。
# 结果是一个字典，键为股票代码（如 'AAPL'），值为该股票的调整后收盘价序列。
# pd.DataFrame(...)：
# 将上述字典转换为一个 Pandas DataFrame，其中每一列代表一只股票的调整后收盘价。
# 结果：
# price 是一个 DataFrame，结构如下：
#                AAPL        IBM       MSFT       GOOG
#     Date
#     2023-01-01   150.23     140.12      300.45     2800.67
#     2023-01-02   151.23     141.12      301.45     2801.67

volume = pd.DataFrame({ticker: data['Volume']
                      for ticker, data in all_data.items()})
# 提取每只股票的成交量（Volume），并将其存储到一个新的 DataFrame 中。
# 具体步骤：
# {ticker: data['Volume'] for ticker, data in all_data.items()}：
# 遍历 all_data 字典，提取每只股票的 Volume 列（成交量）。
# 结果是一个字典，键为股票代码（如 'AAPL'），值为该股票的成交量序列。
# pd.DataFrame(...)：
# 将上述字典转换为一个 Pandas DataFrame，其中每一列代表一只股票的成交量。

# 计算价格的百分数变化,时间序列
returns = price.pct_change()
# 计算股票价格的百分比变化（即收益率）。
# 具体步骤：
# price.pct_change()：
# 调用 Pandas 的 pct_change 方法，计算每一列中相邻两个时间点的百分比变化。
# 公式为：(当前值 - 上一值) / 上一值。
# 对于每只股票的价格序列，计算其每日收益率。
print(returns.tail())

# Series 的corr方法用于计算两个Series之间的相关系数。重叠的非NA的 按索引对齐的值的相关系数。 cov用于计算协方差
returns['MSFT'].corr(returns['IBM'])
returns['MSFT'].cov(returns['IBM'])
# 计算两个Series之间的相关系数和协方差
returns.MSFT.corr(returns.IBM)
# DataFrame的corr和cov方法将以DataFrame的形式分别返回完整的相关系数或协方差矩阵：
returns.corr()
returns.cov()

# 唯一值 值计数 以及成员资格
obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
uniques = obj.unique() # 返回数组中的唯一值
print(uniques)

# 返回的唯一值是未排序的,如果需要的话 可以对结果再次进行排序 uniqes.sort()
# value_counts 用于计算一个series中各个值出现的频率
obj.value_counts()

# 查看 结果 Series 是按值频率降序排序的 value_counts 还是一个顶级pandas 方法可以用于任何数组或序列
pd.value_counts(obj.values, sort=False)

# isin 用于测试一个series中的值是否在另一个序列中 返回一个布尔型数组 用于判断矢量化集合的成员资格 用于过滤Series中或DataFrame列中数据的子集
mask = obj.isin(['b', 'c']) # 返回一个布尔型数组
# isin
# 计算一个表示“Series各值是否包含于传入的值序列中”的布尔型数组
# match
# 计算一个数组中的各值到另一个不同值数组的整数索引；对于数据对齐和连接类型的操作十分有用
# unique
# 计算Series中的唯一值数组，按发现的顺序返回
# value_counts
# 返回一个Series,其索引为唯一值，其值为频率，按计数值降序排列


read_csv
从文件、UL、文件型对象中加载带分隔符的数据。默认分隔符为逗号

read_table
从文件、UL、文件型对象中加载带分隔符的数据。默认分隔符为制表符(t)

read fwf
读取定宽列格式数据（也就是说，没有分隔符）

read_clipboard
读取剪贴板中的数据，可以看做read table的剪贴板版。再将网页转换为表格时很有用

read_excel
从Excel XLS或XLSX file读取表格数据

read hdf
读取pandas写的HDF5文件

read _html
读取HTL文档中的所有表格

read_json
读取JSON(JavaScript Object Notation)字符串中的数据

read_msgpack
二进制格式编码的pandas数据

read _pickle
读取Python pickle格式中存储的任意对象

read_sas
读取存储于SAS系统自定义存储格式的SAS数据集

read_sql
(使用SQLAlchemy)读取SQL查询结果为pandas的DataFrame

read_stata
读取Stata文件格式的数据集

read_feather
读取Feather二进制文件格式

函数的选项可以划分以下几个大类
索引：将一个或多个列当做返回的DataFrame处理，以及是否从文件、用户获取列名。
类型推断和数据转换：包括用户定义值的转换、和自定义的缺失值标记列表等。
日期解析：包括组合功能，比如将分散在多个列中的日期时间信息组合成结果中的单个列。
迭代：支持对大文件进行逐块迭代。
不规整数据问题：跳过一些行、页脚、注释或其他一些不重要的东西（比如由成千上万个逗号隔开的数值数据）。

# pandas 用于将表格型数据读取为DataFrame对象的函数 read_csv和read_table 读取CSV文件
# df = pd.read_csv('E:\code1\data-analysis\data\ex1.csv') # 读取CSV文件
# print(df)
# # read_table 读取CSV文件 使用sep参数指定分隔符 可以使用逗号、制表符、空格等
# df1 = pd.read_table('E:\code1\data-analysis\data\ex1.csv', sep=',')
# print(df1)

# 读取没有标题行的CSV文件时 有两种方法：
# 1.使用header=None参数，将DataFrame的列名设置为0到n-1，其中n是列的数量。
# 2.使用names参数，将DataFrame的列名设置为一个列表。
# df2 = pd.read_csv('..\data\ex2.csv', header=None)
# print(df2)
# df3 = pd.read_csv('..\data\ex2.csv', names=['a', 'b', 'c','d', 'message'])
# print(df3)

# 如果希望将message列做成DataFrame的索引,可以明确表示要将该列放到索引4的位置上 并使用index_col参数指定message列
# names = ['a','b','c','d','message']
# df4 = pd.read_csv('..\data\ex2.csv', names=names, index_col='message')
# print(df4)


# 如果希望将多个列做成一个层次化索引,只需要传入由列编号或列名组成的列表即可
# df5 = pd.read_csv('..\data\csv_mindex.csv', index_col=['key1','key2'])
# print(df5)

# 如果表格不是用固定的分隔符去分隔字段 比如空白符或者其他模式
# 可以传递一个正则表达式作为read_table的分隔符 可以用正则表达式 表达为 \s+
# result = pd.read_table('..\data\ex3.txt', sep='\s+')
# print(result)

# 可以使用skiprows跳过文件的第一行 第二行 第四行
# df = pd.read_csv('..\data\ex4.csv', skiprows=[0,2,3])
# print(df)

# 缺失值处理是文件解析任务中一个重要部分, 缺失数据要么没有 空字符串 要么用某个标记值表示 用NA或NULL 默认情况下 pandas会尝试将缺失值识别为NaN
# result = pd.read_csv('..\data\ex5.csv')
# print(result)
# print(pd.isnull(result)) # isnull返回一个布尔数组 用于就测数据中的缺失值 NaN或None 如果存在缺失值则返回True 如果不存在缺失值则返回False

# na_values可以用一个列表或集合的字符串表示缺失值
result = pd.read_csv('..\data\ex5.csv', na_values=['NULL'])
print(result)

# 字典的键是列名,值是缺失值列表 使用各列标记不同的缺失值
sentinels = {'message': ['foo', 'NA'], 'something': ['two']}
result1 = pd.read_csv('..\data\ex5.csv', na_values=sentinels)
print(result1)

常用的read_csv/read_table 函数的参数
path
表示文件系统位置、UL、文件型对象的字符串
sep或delimiter
用于对行中各字段进行拆分的字符序列或正则表达式
header
用作列名的行号。默认为0（第一行)，如果没有neader?行就应该设置
为None
index col
用作行索引的列编号或列名。可以是单个名称/数字或由多个名称/数字
组成的列表（层次化索引）
names
用于结果的列名列表，结合header-=None
skiprows
需要忽略的行数（从文件开始处算起)，或需要跳过的行号列表（从0
开始)
na values
一组用于替换NA的值
comment
用于将注释信息从行尾拆分出去的字符（一个或多个）

parse_dates
尝试将数据解析为日期，默认为False。如果为True,则尝试解析所
有列。此外，还可以指定需要解析的一组列号或列名。如果列表的元
素为列表或元组，就会将多个列组合到一起再进行日期解析工作（例
如，日期/时间分别位于两个列中)
keep date col
如果连接多列解析日期，则保持参与连接的列。默认为False。
converters
由列号/列名跟函数之间的映射关系组成的字典。例如，'foo':f}会对
foo列的所有值应用函数f
dayfirst
当解析有歧义的日期时，将其看做国际格式（例如，7/6/2012一June
7,2012)。默认为False
date parser
用于解析日期的函数
nrows
需要读取的行数（从文件开始处算起)》
iterator
返回一个TextParserl以便逐块读取文件
chunksize
文件块的大小（用于迭代）
skip_footer
需要忽略的行数（从文件末尾处算起）

verbose
打印各种解析器输出信息，比如“非数值列中缺失值的数量”等
encoding
用于unicode的文本编码格式。例如，“utf-8”表示用UTF-8编码的
文本
squeeze
如果数据经解析后仅含一列，则返回Series
thousands
千分位分隔符，如“，”或“”

# 逐块读取文本文件 处理大文件时 或找出大文件中的参数集以便于后续处理时 如果只想读取文件的一小部分或逐块文件进行迭代
# 在看大文件时可以先设置 pandas显得更紧些
# pd.options.display.max_rows = 10 # 设置显示行数
# 只想读取几行 避免读取整个文件 通过nrows 进行指定即可
# df = pd.read_csv('..\data\ex6.csv', nrows=5)
# print(df)

# 逐块读取文件 可以指定chunksize 行数
chunker = pd.read_csv('..\data\ex6.csv', chunksize=10)
print(chunker)

# read_csv所返回的这个TextParser对象可以根据 chunksize 对文件进行逐块迭代  我们可以迭代处理ex6.csv 将值计数聚合到key列中
tot = pd.Series([])
for piece in chunker:
    tot = tot.add(piece['key'].value_counts(), fill_value=0)

tot = tot.sort_values(ascending=False)
print(tot[:10])

# 将数据写出到文本格式 数据也可以输出为分隔符格式的文本
data = pd.read_csv('..\data\ex5.csv')
print(data)

# 使用DataFrame的to_csv方法将数据写出到文本文件 将数据写到一个以逗号分隔的文件中
data.to_csv('..\data\out.csv')
data1 = pd.read_csv('..\data\out.csv')
print(data1)

data.to_csv(sys.stdout, sep='|')
# 缺失值在输出结果中会被表示为空字符串 可以使用na_rep参数指定一个替换值
data.to_csv(sys.stdout, na_rep='NULL')
# 如果没有设置其他选项 会写出行和列名 也可以被禁用
data.to_csv(sys.stdout, index=False, header=False)

# 只写出一部分的列并指定的顺序排列
data.to_csv(sys.stdout, index=False, columns=['a', 'b', 'c'])

# 使用series的to_csv方法将数据写出到文本文件
dates = pd.date_range('1/1/2000', periods=7) # 生成日期序列
ts = pd.Series(np.arange(7), index=dates) # 生成时间序列
ts.to_csv('../data/tseries.csv')
ts1 = pd.read_csv('../data/tseries.csv')
print(ts1)


# 处理分隔符格式 处理某些畸形行的文件
# 任何单字符分隔符文件,可以直接使用python 的csv 模块 将任意已打开的文件或文件型的对象传给 csv.reader
# f = open('..\data\ex7.csv')
# reader = csv.reader(f)
# # 对这个reader 进行迭代将会为每行产生一个元组并且移除所有的引号
# for line in reader:
#     print(line)

# 数据处理 首先读取数据到一个多行的列表中
with open('..\data\ex7.csv') as f:
    lines = list(csv.reader(f))

# 将这些行分为 标题行和数据行
header,values = lines[0],lines[1:]

# 用字典构造式和zip(*values)函数将标题行和数据行组合在一起 后者将行转置为列 创建数据列的字典
data_dict = {h:v for h,v in zip(header,zip(*values))}
print(data_dict)

# csv文件的形式有很多,只需定义 csv.Dialect的一个子类即可定义出新格式 如专门的分隔符,字符串引用约定,行结束符等

class my_dialect(csv.Dialect):
    '''
    自定义CSV方言类，用于规定CSV文件的格式。

    该类重写了csv.Dialect的几个属性，以满足特定的CSV文件解析需求。
    - lineterminator: 设置每行的终止符为换行符('\n')。
    - delimiter: 设置字段间的分隔符为分号(';')。
    - quotechar: 设置字段内的引用符为双引号('"')。
    - quoting: 设置引用字段的规则为仅在必要时引用(csv.QUOTE_MINIMAL)。
    '''
    lineterminator = '\n'
    delimiter = ';'
    quotechar = '"'
    quoting = csv.QUOTE_MINIMAL

# 使用自定义方言类创建CSV阅读器


# 使用csv.writer类将字典数据写入CSV文件
# 创建一个CSV文件并写入标题行
with open('..\data\ex7.csv') as f:
    reader = csv.reader(f, dialect=my_dialect)
    for line in reader:
        print(line)
    # writer = csv.writer(f, dialect=my_dialect)
    # writer.writerow(('one', 'two', 'three')) # 写入一行数据


# 各个csv语支的参数也可以用关键字的形式提供给 csv.reader 无需定义子类
# reader = csv.reader(f,delimiter='|')


delimiter
用于分隔字段的单字符字符串。默认为“，”
lineterminator
用于写操作的行结束符，默认为“八n”。读操作将忽略此选项，它能
认出跨平台的行结束符
quotechar
用于带有特殊字符（如分隔符)的字段的引用符号。默认为
quoting
引用约定。可选值包括csv.QUOTE_ALL（引用所有字段)、csv.
QUOTE_MINIMAL（只引用带有诸如分隔符之类特殊字符的字段)
、
csV.QUOTE_NONNUMERIC以及csv.QUOTE_NON(不引用)。完整信息
请参考Python的文档。默认为QUOTE_MINIMAL
skipinitialspace
忽略分隔符后面的空白符。默认为False
doublequote
如何处理字段内的引用符号。如果为True,则双写。完整信息及行为
请参见在线文档
escapechar
用于对分隔符进行转义的字符串（如果quoting被设置为csv.QUOTE
NONE的话)。默认禁用

class my_dialect(csv.Dialect):
    '''
    自定义CSV方言类，用于规定CSV文件的格式。

    该类重写了csv.Dialect的几个属性，以满足特定的CSV文件解析需求。
    - lineterminator: 设置每行的终止符为换行符('\n')。
    - delimiter: 设置字段间的分隔符为分号(';')。
    - quotechar: 设置字段内的引用符为双引号('"')。
    - quoting: 设置引用字段的规则为仅在必要时引用(csv.QUOTE_MINIMAL)。
    '''
    lineterminator = '\n'
    delimiter = ';'
    quotechar = '"'
    quoting = csv.QUOTE_MINIMAL

# 手工输出分隔符文件 使用csv.writer 接受一个已打开且可写的文件对象 以及跟csv.reader一样的参数
with open('..\data\ex7.csv', 'w', newline='') as f:  # 以写模式打开文件
    writer = csv.writer(f, dialect=my_dialect)
    writer.writerow(('one', 'two', 'three'))
    writer.writerow(('four', 'five', 'six'))
    writer.writerow(('seven', 'eight', 'nine'))
    writer.writerow(('ten', 'eleven', 'twelve'))


# 如果需要读取文件内容，可以重新打开文件
with open('..\data\ex7.csv', 'r') as f:
    datas = csv.reader(f, dialect=my_dialect)
    for line in datas:
        print(line)

# json数据  JSON（JavaScript Object Notation的简称）已经成为通过HTTP请求在Web浏览器和其他应用程序之间发送数据的标准格式之一。它是一种比表格型文本格式（如CSV）灵活得多的数据格式 除其空值null和一些其他的细微差别（如列表末尾不允许存在多余的逗号）之外，JSON非常接近于有效的Python代码。基本类型有对象（字典）、数组（列表）、字符串、数值、布尔值以及null。对象中所有的键都必须是字符串。许多Python库都可以读写JSON数据。我将使用json，因为它是构建于Python标准库中的。通过json.loads即可将JSON字符串转换成Python形式：
obj =  '''
{"name": "Wes",
 "places_lived": ["United States", "Spain", "Germany"],
 "pet": null,
 "siblings": [{"name": "Scott", "age": 30, "pets": ["Zeus", "Zuko"]},
              {"name": "Katie", "age": 38,
               "pets": ["Sixes", "Stache", "Cisco"]}]
}
'''

# json.loads() 将JSON字符串转换成Python形式
result = json.loads(obj)
print(result)

# json.dumps() 将Python形式转换成JSON字符串
asjson = json.dumps(result)
print(asjson)

# 将一个json对象转换成DataFrame 最简单的方式是 向DataFrame构造器传入一个字典的列表 就是原先的json对象,并选取数据字段的子集
siblings = pd.DataFrame(result['siblings'], columns=['name', 'age'])
print(siblings)

# pandas.read_json 可以自动将特别格式的json数据转换为 Series 或 DataFrame
data = pd.read_json('..\data\example.json') #读取json文件
print(data)
# 将数据从pandas输出到json 可以使用to_json()方法
print(data.to_json())
print(data.to_json(orient='records')) #将数据转换为json字符串 orient参数指定了数据格式，默认为columns

# xml和html Python有许多可以读写常见的HTML和XML格式数据的库，包括lxml、Beautiful Soup和html5lib。lxml的速度比较快，但其它的库处理有误的HTML或XML文件更好。
# pandas有一个内置的功能，read_html，它可以使用lxml和Beautiful Soup自动将HTML文件中的表格解析为DataFrame对象。
# 安装 pip install lxml ,pip install beautifulsoup4 html5lib
tables = pd.read_html('..\data\fdic_failed_bank_list.html')
print(len(tables)) # 输出结果为1
failures = tables[0] # 输出结果为DataFrame对象
print(failures.head()) # 输出结果为前5行

# 按时间戳对数据进行排序
close_timestamps = pd.to_datetime(failures['Closing Date'])
close_timestamps.dt.year.value_counts() #按年份进行分组统计

"""
from fileinput import close

import numpy as np
import pandas as pd
import pandas_datareader.data as web
import sys
import csv
import json

from anyio import value
from whatthepatch.patch import header
from lxml import objectify


# 利用lxml.objectify解析XML
# 定义XML文件的路径
path = '..\data\Performance_MNR.xml'

# 使用objectify解析XML文件，这里使用open函数打开文件，然后parse函数解析
parsed = objectify.parse(open(path))

# 获取解析后的XML的根元素
root = parsed.getroot()

# 初始化一个空列表用于存储解析后的数据
data = []

# 定义需要跳过的字段，这些字段不会被包含在最终的数据字典中
skip_fields = ['PARENT_SEQ', 'INDICATOR_SEQ',
               'DESIRED_CHANGE', 'DECIMAL_PLACES']

# 遍历XML中的INDICATOR元素，每个INDICATOR元素代表一条记录
for elt in root.INDICATOR:
    # 初始化一个字典用于存储当前记录的数据
    el_data = {}

    # 遍历当前INDICATOR元素的所有子元素
    for child in elt.getchildren():
        # 如果子元素的标签不在需要跳过的字段列表中，则将其标签和值添加到字典中
        if child.tag not in skip_fields:
            el_data[child.tag] = child.pyval
            continue
        # 如果子元素的标签在需要跳过的字段列表中，也将其标签和值添加到字典中
        # 这行代码是多余的，应该去掉
        el_data[child.tag] = child.pyval

    # 将当前记录的数据字典添加到数据列表中
    data.append(el_data)

# 将解析后的数据转换为DataFrame，以便于后续的数据处理
perf = pd.DataFrame(data)

# 打印DataFrame，查看解析后的数据
print(perf)


if __name__ == '__main__':
    pass